/*
    游戏网关: 新玩家注册,登录(获取密钥,World/Game服务器地址),登出
 */
var http    = require('http');
var https   = require('https');
var fs      = require('fs');
var url     = require('url');
var util    = require('util');
var Iconv   = require('iconv').Iconv;
var exec    = require('child_process').exec;
var async   = require('async');
var mysql   = require('mysql');
var crypto  = require('crypto');
var mongodb = require('mongodb');
var zlib    = require('zlib');
var events = require('events');
var path    = require('path');

global.clone    = require('clone');
global.common   = require('./common.js');
global.config   = require(fs.existsSync('../config.js') ? '../config.js' : './config.js');

global.gmConfig = require(fs.existsSync('../config.gm.js') ? '../config.gm.js' : './config.gm.js');

var pay         = require('./pay.js');
var server      = require('./server.js');
var logger  = require('./logger.js');

require('./global.js');
global.gServerName = 'gateway';
global.gDBPlat = null;
global.gDBUser = null;
global.gDBPay = null;
global.gDBAnalysis = null;
global.gUsers = {};     // 所有用户openid => uid 映射关系
global.gBlocks = {};    // 所有禁止登陆openid => stamp
global.gOpenkeys = {};  // 玩家openid=>openkey
global.gMutiUsers = {}; // 合区后玩家openid => uids 映射关系
global.gMutiUserUidCaches = {};

logger.init({name:'gateway', debug:{backups:5}, error:{backups:1}});
(function main(){
    server.loadConf();
    server.loadDB(function(db){
        INFO('mongodb connected');

        global.gDBPlat = db.collection('plat');
        global.gDBUser = db.collection('user');
        global.gDBPay = db.collection('pay');
        global.gDBAnalysis = db.collection('analysis');
        
        var cursor = gDBPlat.find();
        cursor.each(function(err, item){
            if( err ) {
                ERROR(err);
                process.exit(-1);
            }
            if( item ) {
                gUsers[item._id] = +item.uid;
                if( item.block ) {
                    gBlocks[item._id] = item.block;
                }

                if( item.uids ) {
                    var uids = {};
                    item.uids.forEach(function(uid) {
                        uids[gGetSidFromUid(+uid)] = uid; 
                    });
                    gMutiUsers[item._id] = uids;
                }
            }

            if( cursor.isClosed() ) {
                startWebServer();
                config.Pay && pay.startWebServer();
            }
        });
    }, 2);
})();

function getHttpClientIP(req) {
    return req.headers['x-forwarded-for'] ||
        req.connection.remoteAddress ||
        req.socket.remoteAddress ||
        req.connection.socket.remoteAddress;
}

function startWebServer(){
    // 创建web服务器
    var httpsHandler = function(req, res) {
        res._https = true;        
        httpHandler(req, res);
    }

    var httpHandler = function(req, res){

        // 跨域配置
        res.setHeader("Access-Control-Allow-Origin", "*");
        res.setHeader("Access-Control-Allow-Headers", "X-Requested-With");
        res.setHeader("Access-Control-Allow-Methods","GET");

        if( req.method != 'GET' ) { // 支持GET请求
            req.connection.destroy();
            return;
        }
        var reqObj = url.parse(req.url, true);
        var query = reqObj.query || {};
        var path = reqObj.pathname;

        req.on('end', function() {

            if( path.endWith('/crossdomain.xml') ) {
                res.writeHead(200, common.defaultHeaders);
                res.end(common.defaultCrossDomain);
                return;
            }else if( path.endWith('.html') || path.endWith('.js')
                || path.endWith('.css') || path.endWith('.gif') ) {
                //var segs = path.split('/');
                //handleStaticFile('web/'+segs[segs.length-1], res);
                //handleStaticFile('web' + path, res);
                handleWeb(req, res, './web');
                return;
            }else if( path.endWith('.dat')) {
                var segs = path.split('/');
                handleStaticFile('conf/'+segs[segs.length-1], res, true);
                return;
            }else if( path.endWith('/conf.zip') ) {
                handleStaticFile('conf.zip', res);
                return;
            }else if( path.endWith('/update_conf') && config.UpdateConf ) {
                handleUpdateConf(res);
                return;
            }
            query.clientIP = getHttpClientIP(req);
            handleReq(query, res);
        });

        req.resume();
        
    };

    var pidFile = 'gateway.pid';
    process.on('SIGINT', function() {
        exit();
    });

    process.on('SIGTERM', function() {
        exit();
    });

    process.on('uncaughtException', function (err) {
        ERROR(err.stack);
    });

    function exit() {
        INFO('gateway shutdown');
        fs.existsSync(pidFile) && fs.unlinkSync(pidFile);
        process.exit();
    }

    var server = http.createServer(httpHandler).listen(config.GatewayListen, function(){
        INFO('gateway start');
        fs.writeFileSync(pidFile, process.pid, 'utf8');
    });

    server.on('error', function(e){
        if (e.code == 'EADDRINUSE') {
            ERROR(e);
            process.exit(-1);
        } else {
            ERROR(e);
        }
    })

    if( config.HttpsKey && config.HttpsCrt ) {
        var options = {
            key: fs.readFileSync(config.HttpsKey, 'utf8'),
            cert: fs.readFileSync(config.HttpsCrt, 'utf8')
        };

        https.createServer(options, httpsHandler).listen(config.GatewayListen-4000);
    }
}

function handleReq(query, res) {
    var code = 1;
    var desc = '';
    
    if( !query.act ) {
        desc = 'no act';
    }else if( !logic[query.act] ) {
        desc = 'act ' + query.act + ' not support';
    }else{
        code = 0;
    }

    var resp = {
        'code': code, 
        'desc': desc, 
        'data':{}
    };

    if( resp.code != 0 ) {
        onReqHandled(res, resp);
    }else {
        logic[query.act](query, res, resp);
    }
}

// -------------------处理静态文件访问---------------------------------

var staticFilePool = {};
function handleStaticFile(file, res, convertUTF8) {
    if( file in staticFilePool ) {
        sendStaticFile(file, staticFilePool[file], res, convertUTF8);
    }else {
        fs.exists(file, function(exists){
            if( exists  ) {
                fs.readFile(file, function(err, data){
                    staticFilePool[file] = data;
                    sendStaticFile(file, staticFilePool[file], res, convertUTF8);
                });
            }else {
                res.writeHead(404, common.defaultHeaders);
                res.end();
            }
        });
    }
}

function sendStaticFile(file, content, res, convertUTF8) {
    if( convertUTF8 ) {
        var converter = new Iconv('GBK', 'UTF8');
        content = converter.convert(content).toString();
    }
    if( file.endWith('html') || file.endWith('htm') ) {
        res.writeHead(200, common.htmlHeaders);
    } else if (file.endWith('css')) {
        res.writeHead(200, {'Content-Type': 'text/css;'});
    } else if (file.endWith('gif')){
        res.writeHead(200, {'Content-Type': 'image/gif;'});
    } else{
        /*
        var expire = new Date();
        expire.setTime(expire.getTime() + 8640000*1000);
        res.setHeader('Expires', expire.toUTCString());
        res.setHeader('Cache-Control', 'max-age=8640000');
        */

        res.writeHead(200, common.defaultHeaders);
    }
    res.end(content);
}

function handleUpdateConf(res) {
    res.writeHead(200, common.htmlHeaders);
    res.end('成功');

    var pwd = process.cwd(); 
    exec(pwd + '/replace_confs ' + pwd);
}

function handleWeb(req, resp, docRoot) {
    var realPath = path.resolve(path.join(docRoot, url.parse(req.url).pathname));
    fs.exists(realPath, function(exists){
        if (!exists) {
            error404(req, resp);
        } else {
            fs.stat(realPath, function(err, stat){
                if (err) error500(req, resp, err);
                else {
                    if (stat.isFile()) {
                        handleFile(req, resp, realPath, stat);
                    } else {
                        error500(req, resp, 'Unsupported type');
                    }
                }
            });
        }
    });

    function error404(req, resp) {
        resp.writeHead(404);
        resp.end('404');
    }

    function error500(req, resp, err) {
        resp.writeHead(500);
        resp.end(util.format('%j', err));
    }

    function handleFile(req, resp, fpath, stat) {
        var lastModified = stat.mtime.toUTCString();
        resp.setHeader('Last-Modified', lastModified);
        var ifModifiedSince = 'If-Modified-Since'.toLowerCase();
        if (req.headers[ifModifiedSince] && (req.headers[ifModifiedSince] == lastModified)) {
            resp.writeHead(304);
            resp.end();
        } else {
            resp.writeHead(200, 'Ok', {
                'Content-Encoding': 'gzip',
                'Last-Modified': lastModified
            });
            fs.createReadStream(fpath).pipe(zlib.createGzip()).pipe(resp);
        }
    }
}

// --------------------请求逻辑处理-----------------------------------

function onReqHandled(res, data) {
    res.writeHead(200, common.defaultHeaders);
    res.end(JSON.stringify(data));
}

var logic = {};
// 登录,如果是新用户完成注册
logic.login = function(req, res, resp) {
    do {
        if( !req.openid || !req.openkey || !req.user) {
            resp.code = 1; resp.desc = 'no openid or openkey'; break;
        }

        var openid = req.openid;
        if( !common.verifyGatewayAuth(openid+req.user, req.openkey) 
            && (req.openkey != config.GWAuth)) {
            resp.code = 1; resp.desc = 'openkey verify fail'; break;
        }
        
        var now = Date.getStamp();
        var blockStamp = gBlocks[openid];
        if (blockStamp && now < blockStamp) {
            resp.data.stamp = blockStamp;
            resp.code = 1; resp.desc = 'blocked_user'; break;
        }
    }while(false);

    if( resp.code != 0 ) {
        onReqHandled(res, resp);
        return;
    }
    // 缓存玩家openkey,以便后续调用支付API
    if( req.openkey_qq ) {
        gOpenkeys[openid] = req.openkey_qq;
    }

    var existUid = gUsers[openid];
    if( existUid ) {
        var uids = gMutiUsers[openid];
        if (req.sid && uids) {
            var uid = uids[+req.sid];
            if (uid && (uid != existUid)) {
                if (!gMutiUserUidCaches.hasOwnProperty(uid)) {
                    gDBUser.find({_id:uid}, {'_id':1}).toArray(function(err, arr) {
                        if (!err && arr.length > 0) {
                            gMutiUserUidCaches[uid] = true;
                            existUid = uid;
                        } else {
                            gMutiUserUidCaches[uid] = false;
                        }
                        onLogIn(req, res, resp, existUid);
                    });
                    return;
                } else {
                    if (gMutiUserUidCaches[uid]) {
                        existUid = uid;
                    }
                }
            }
        }
        onLogIn(req, res, resp, existUid);
    }else{
        // 新注册用户
        gDBPlat.findAndModify({_id:'_userid'}, [], {$inc:{'ai':1}}, 
                                {'new':true}, function(err, doc) {
            if( doc && !err ) {
                var newUID = doc.ai;
                gDBPlat.insert({_id: openid, uid: newUID, time:common.getTime()}, 
                    function(err, result){
                    if( err ) {
                        resp.code = 1; resp.desc = 'on create';
                        onReqHandled(res, resp);
                    }else{
                        gUsers[openid] = newUID;
                        onLogIn(req, res, resp, newUID); 
                        reportQQ('register', {'opuid': newUID}, req.pf);
                    }
                });
            }else{
                resp.code = 1; resp.desc = 'generate id';
                onReqHandled(res, resp);
            }
        });
    }
};

logic.buy_goods = function(req, res, resp) {
    do {
        if( !req.openid || isNaN(req.gid) || !req.pf || !req.pfkey) {
            resp.code = 1; resp.desc = 'no openid or gid'; break;
        }
        
        var gid = +req.gid;
        var pf = req.pf || 'qzone';
        var pfkey = req.pfkey;

        var openid = req.openid;
        var openkey = gOpenkeys[openid];
        if( !openkey ) {
            resp.code = 1; resp.desc = 'not login'; break;
        }

        pay.buyGoods(openid, openkey, pf, pfkey, gid, function(data){
            if( data && data.ret==0 && data.url_params ) {
                resp.data.url = data.url_params;
            }else{
                resp.code = 1;
            }

            onReqHandled(res, resp);
        }, req.manyouid);
        
        return;
    }while(false);

    onReqHandled(res, resp);
};

logic.task_complete = function(req, res, resp) {
    do {
        if( !req.openid || !req.contractid || !req.pf || !req.pfkey) {
            resp.code = 1; resp.desc = 'no openid or contractid'; break;
        }
        
        var gid = +req.gid;
        var pf = req.pf || 'qzone';
        var pfkey = req.pfkey;
        var contractid = req.contractid;

        var openid = req.openid;
        var openkey = gOpenkeys[openid];
        if( !openkey ) {
            resp.code = 1; resp.desc = 'not login'; break;
        }

        pay.informTaskComplete(openid, openkey, pf, pfkey, contractid, function(data){
            if( data && data.ret==0 ){
            }else{
                //resp.code = 1;
            }

            onReqHandled(res, resp);
        });
        
        return;
    }while(false);

    onReqHandled(res, resp);
};

logic.yellow_first = function(req, res, resp) {
    do {
        if( !req.openid || !req.openkey || !req.pf || !req.user) {
            resp.code = 1; resp.desc = 'no openid or openkey'; break;
        }
        
        if( !common.verifyGatewayAuth(req.openid+req.user, req.openkey) 
            && (req.openkey != config.GWAuth)) {
            resp.code = 1; resp.desc = 'openkey verify fail'; break;
        }

        var openid = req.openid;
        var openkey = gOpenkeys[openid];
        if( !openkey ) {
            resp.code = 1; resp.desc = 'not login'; break;
        }

        var existUid = gUsers[openid];
        if( !existUid ) {
            resp.code = 1; resp.desc = 'openid invalid '; break;
        }

        try{
            var user = JSON.parse(req.user);
        }catch(e){
            resp.code = 1; resp.desc = 'invalid user'; break;
        }
        
        if( !user.is_yellow_vip || +user.yellow_vip_level <= 0) {
            resp.code = 1; resp.desc = 'not yellow user'; break;
        }

        var isBlue = user.is_blue ? 1:0;

        // 请求game数据
        var url = util.format('%s?mod=gm&act=yellow_first&uid=%d&args=%j',
                getGameServer(existUid), existUid, {key:config.GMAuth, is_blue:isBlue});

        common.httpGet(url, function(data){
            if( data && data.code == 0 ) {
                if(data.hasOwnProperty('args')) {
                    delete data.args;
                }
                resp = data;
            }else{
                resp.code = 1;
                resp.desc = 'request game error';
            }

            onReqHandled(res, resp);
        }, true);
        
        return;
    }while(false);

    onReqHandled(res, resp);
};

logic.yellow_daily = function(req, res, resp) {
    do {
        if( !req.openid || !req.openkey || !req.pf || !req.user) {
            resp.code = 1; resp.desc = 'no openid or openkey'; break;
        }

        if( !common.verifyGatewayAuth(req.openid+req.user, req.openkey) 
            && (req.openkey != config.GWAuth)) {
            resp.code = 1; resp.desc = 'openkey verify fail'; break;
        }

        var openid = req.openid;
        var openkey = gOpenkeys[openid];
        if( !openkey ) {
            resp.code = 1; resp.desc = 'not login'; break;
        }

        var existUid = gUsers[openid];
        if( !existUid ) {
            resp.code = 1; resp.desc = 'openid invalid '; break;
        }

        try{
            var user = JSON.parse(req.user);
        }catch(e){
            resp.code = 1; resp.desc = 'invalid user'; break;
        }

        if( !user.is_yellow_vip || +user.yellow_vip_level <= 0 ) {
            resp.code = 1; resp.desc = 'not yellow user'; break;
        }

        // 请求game数据
        var args = {
            key: config.GMAuth, 
            level: user.yellow_vip_level, 
            year: user.is_yellow_year_vip, 
            super: user.is_super_yellow_vip,
            is_blue: user.is_blue ? 1:0,
        };
        var url = util.format('%s?mod=gm&act=yellow_daily&uid=%d&args=%j',
                getGameServer(existUid), existUid, args);

        common.httpGet(url, function(data){
            if( data && data.code == 0 ) {
                if(data.hasOwnProperty('args')) {
                    delete data.args;
                }
                resp = data;
            }else{
                resp.code = 1;
                resp.desc = 'request game error';
            }

            onReqHandled(res, resp);
        }, true);
        
        return;
    }while(false);

    onReqHandled(res, resp);
};

logic.blue_level = function(req, res, resp) {
    do {
        if( !req.openid || !req.openkey || !req.pf || !req.user) {
            resp.code = 1; resp.desc = 'no openid or openkey'; break;
        }

        if( !common.verifyGatewayAuth(req.openid+req.user, req.openkey) 
            && (req.openkey != config.GWAuth)) {
            resp.code = 1; resp.desc = 'openkey verify fail'; break;
        }

        var openid = req.openid;
        var openkey = gOpenkeys[openid];
        if( !openkey ) {
            resp.code = 1; resp.desc = 'not login'; break;
        }

        var existUid = gUsers[openid];
        if( !existUid ) {
            resp.code = 1; resp.desc = 'openid invalid '; break;
        }

        try{
            var user = JSON.parse(req.user);
        }catch(e){
            resp.code = 1; resp.desc = 'invalid user'; break;
        }

        if( !user.is_yellow_vip || +user.yellow_vip_level <= 0 || !user.is_blue) {
            resp.code = 1; resp.desc = 'not blue user'; break;
        }

        // 请求game数据
        var args = {key:config.GMAuth, level:req.level};
        var url = util.format('%s?mod=gm&act=blue_level&uid=%d&args=%j',
                getGameServer(existUid), existUid, args);

        common.httpGet(url, function(data){
            if( data && data.code == 0 ) {
                if(data.hasOwnProperty('args')) {
                    delete data.args;
                }
                resp = data;
            }else{
                resp.code = 1;
                resp.desc = 'request game error';
            }

            onReqHandled(res, resp);
        }, true);
        
        return;
    }while(false);

    onReqHandled(res, resp);
};

logic.purview_task = function(req, res, resp) {
    do {
        if( !req.openid || !req.contractid || !req.pf || !req.pfkey) {
            resp.code = 1; resp.desc = 'no openid or contractid'; break;
        }

        var segs = req.contractid.split('_');
        if( segs.length != 3 || segs[0] != 'lostplayer' ) {
            resp.data.qualify = 0; break;
        }
        
        var pf = req.pf || 'qzone';
        var pfkey = req.pfkey;

        var openid = req.openid;
        var openkey = gOpenkeys[openid];
        if( !openkey ) {
            resp.code = 1; resp.desc = 'not login'; break;
        }

        pay.informTaskComplete(openid, openkey, pf, pfkey, segs[1], segs[2], function(data){
            if( data && data.ret==0 && data.qualify ){
                resp.data.qualify = 1;
            }else{
                resp.data.qualify = 0;
            }

            onReqHandled(res, resp);
        });
        
        return;
    }while(false);

    onReqHandled(res, resp);
};

logic.p3366_daily = function(req, res, resp) {
    do {
        if( !req.openid || !req.openkey || !req.pf || !req.user) {
            resp.code = 1; resp.desc = 'no openid or openkey'; break;
        }

        if( !common.verifyGatewayAuth(req.openid+req.user, req.openkey) 
            && (req.openkey != config.GWAuth)) {
            resp.code = 1; resp.desc = 'openkey verify fail'; break;
        }

        var openid = req.openid;
        var openkey = gOpenkeys[openid];
        if( !openkey ) {
            resp.code = 1; resp.desc = 'not login'; break;
        }

        var existUid = gUsers[openid];
        if( !existUid ) {
            resp.code = 1; resp.desc = 'openid invalid '; break;
        }

        try{
            var user = JSON.parse(req.user);
        }catch(e){
            resp.code = 1; resp.desc = 'invalid user'; break;
        }

        if( !user['3366_grow_level'] || +user['3366_grow_level'] <= 0 ) {
            resp.code = 1; resp.desc = 'not 3366  user'; break;
        }

        // 请求game数据
        var args = {key:config.GMAuth, level:+user['3366_grow_level']};
        var url = util.format('%s?mod=gm&act=p3366_daily&uid=%d&args=%j',
                getGameServer(existUid), existUid, args);

        common.httpGet(url, function(data){
            if( data && data.code == 0 ) {
                if(data.hasOwnProperty('args')) {
                    delete data.args;
                }
                resp = data;
            }else{
                resp.code = 1;
                resp.desc = 'request game error';
            }

            onReqHandled(res, resp);
        }, true);
        
        return;
    }while(false);

    onReqHandled(res, resp);
};

logic.get_pay_token = function(req, res, resp) {
    do {
        if( !req.openid || !req.pf || !req.pfkey) {
            resp.code = 1; resp.desc = 'no openid'; break;
        }
        
        var pf = req.pf || 'qzone';
        var pfkey = req.pfkey;

        var openid = req.openid;
        var openkey = gOpenkeys[openid];
        if( !openkey ) {
            resp.code = 1; resp.desc = 'not login'; break;
        }

        pay.getPayToken(openid, openkey, pf, pfkey, function(data){
            if( data && data.ret==0 && data.token ) {
                resp.data.token = data.token;
                resp.data.zoneid = config.ServerId;
            }else{
                resp.code = 1;
            }

            onReqHandled(res, resp);
        });
        
        return;
    }while(false);

    onReqHandled(res, resp);
};

logic.is_app_onpanel = function(req, res, resp) {
    do {
        if( !req.openid || !req.pf ) {
            resp.code = 1; resp.desc = 'no openid'; break;
        }
        
        var pf = req.pf || 'qzone';
    
        var openid = req.openid;
        var openkey = gOpenkeys[openid];
        if( !openkey ) {
            resp.code = 1; resp.desc = 'not login'; break;
        }

        pay.isAppOnPanel(openid, openkey, pf, function(data){
            if( data && data.ret == 0 && data.in_applist ) {
                resp.data.isin = 1;
            }else{
                resp.data.isin = 0;
            }   
            
            onReqHandled(res, resp);
        }); 
        
        return;
    }while(false);
    
    onReqHandled(res, resp);
};

logic.online = function(req, res, resp) {
    var args = {};
    var url = util.format('%s?mod=gm&act=online&uid=1&args=%j',
            getGameServer(1), args);

    common.httpGet(url, function(data){
        res.writeHead(200, common.defaultHeaders);
        res.end('' + (data || 0));
    }, true);
};

// 重置任务集市
logic.reset_task_v3 = function(req, res, resp) {
    var openid = req.openid;
    var existUid = null;
    var result = {code:0, desc:''};

    do {
        if (openid) {
            existUid = gUsers[openid];
        } else if (!isNaN(req.uid)) {
            var uid = +req.uid;
            for( var existOpenid in gUsers ) {
                if( gUsers[existOpenid] == uid ) {
                    existUid = uid;
                    openid = existOpenid;
                    break;
                }
            }
        }

        if (!existUid) {
            result.code = 1; result.desc = 'invalid user'; break;
        }

        var url = util.format('%s?mod=gm&act=reset_task_v3&uid=%d&args=%j', getGameServer(existUid), existUid, req);
        common.httpGet(url, function(data) {
            try{
                data = JSON.parse(data);
            }catch(error){
            }

            if (data) {
                result.code = data.code; result.desc = data.desc;
            } else {
                result.code = 1; result.desc = 'request game failed';
            }

            res.writeHead(200, common.defaultHeaders);
            res.end(JSON.stringify(result));
        });
        return;
    } while (false);

    res.writeHead(200, common.defaultHeaders);
    res.end(JSON.stringify(result));
}

// 玩家获取接口
logic.query_user = function(req, res, resp){
    var openid = req.openid;
    var existUid = null;
    if( openid ) {
        existUid = gUsers[openid];
    }else if( !isNaN(req.uid) ) {
        var uid = +req.uid;
        for( var existOpenid in gUsers ) {
            if( gUsers[existOpenid] == uid ) {
                existUid = uid;
                openid = existOpenid;
                break;
            }
        }
    }

    if( !existUid ) {
        res.writeHead(200, common.defaultHeaders);
        res.end(JSON.stringify({
            'code' : 1,
            'desc' : 'invalid openid'
        }));
        return;
    }

    var url = util.format('%s?mod=gm&act=gm&uid=%d&args=%j', getGameServer(existUid), existUid, req);
    common.httpGet(url, function(data){
        try{
            data = JSON.parse(data);
        }catch(error){
        }

        if( data && data.data && data.data.info ) {
            data.data.info.openid = openid;
            data.data.info.uid = existUid;
        }

        res.writeHead(200, common.defaultHeaders);
        res.end(JSON.stringify(data));
    });

};

// GM管理接口
logic.gm = function(req, res, resp) {

    /**************************************************************************/

    var gmAccount = req["authGM"];
    var authKey = req["loginKey"];
    if (!gmAccount && !authKey) {
        resp.code = -1;
        resp.desc = 'please login';
        res.writeHead(200, common.defaultHeaders);
        res.end(JSON.stringify(resp));
        return;
    }

    var meResp = resp;
    var gmUser = null;
    var modPrivilegeObj = null;
    async.series([
        function(cb) { // 查询用户
            queryGMUser(gmAccount, function(err, res){
                if (res) {
                    gmUser = res;
                }
                cb(null);
            });
        },
        function(cb) { // 登录验证
            checkGMAuth(gmUser, authKey, function(passed){
                if (!passed) {
                    meResp.code = -1;
                    meResp.desc = 'please login';
                    cb("checkGMAuth failed");
                } else {
                    cb(null);
                }
            });
        },
        function(cb) { // 查询模块权限配置
            loadAllModelPrivilege(function(err, res){
                modPrivilegeObj = res;
                cb(null);
            });
        },
        function(cb) { // 权限验证
            checkGMPrivilege(gmUser, 'gm', req.method,
                modPrivilegeObj, function(passed){
                    if (!passed) {
                        meResp.code = -2;
                        meResp.desc = 'no access privilege';
                        cb("checkGMPrivilege failed");
                    } else {
                        cb(null);
                    }
            });
        }
    ], function(err){
        if (err) { // 登录或者权限验证失败
            LOG(err);
            res.writeHead(200, common.defaultHeaders);
            res.end(JSON.stringify(meResp));
            return;
        }

        ///////////////////////////////////////////////////////////////////

        // 批量发道具
        if (req.method == 'batSend') {
            _batSend(gmUser, req, res, {code:0, desc:'', data:{}});
            return;
        }

        var openid = req.openid;
        var existUid = null;
        if( openid ) {
            existUid = gUsers[openid];
        }else if( !isNaN(req.uid) ) {
            var uid = +req.uid;
            for( var existOpenid in gUsers ) {
                if( gUsers[existOpenid] == uid ) {
                    existUid = uid;
                    openid = existOpenid;
                    break;
                }
            }
        }

        if( !existUid ) {
            res.writeHead(200, common.defaultHeaders);
            res.end('invalid openid');
            return;
        }

        if( req.key != config.GMAuth ) {
            res.writeHead(200, common.defaultHeaders);
            res.end('invalid key');
            return;
        }

        // 记录GM动作
        logGMActive({
            gmUser: gmUser,
            ip: req["clientIP"],
            method: req.method,
            req: req,
            openid: req.openid,
            uid: existUid
        });

        // GM mod=gm,act=gm,method
        // 解封
        if( req.method == 'unblock' ) {
            if( gBlocks[openid] ) {
                delete gBlocks[openid];
                gDBPlat.update({_id:openid},{$unset:{'block':1}}, function(err, doc){
                    res.writeHead(200, common.defaultHeaders);
                    res.end(err ? '失败' : '成功');
                });
            }else{
                res.writeHead(200, common.defaultHeaders);
                res.end('成功');
            }
            return;
        }

        // 封号
        if( req.method == 'block' ) {
            var blockStamp = Math.max(Date.getStamp(), gBlocks[openid]||0);
            blockStamp += (+req.blocktime)||(86400*30);
            gBlocks[openid] = blockStamp;
            gDBPlat.update({_id:openid},{$set:{'block':blockStamp}}, function(err, doc){
                res.writeHead(200, common.defaultHeaders);
                res.end(err ? '失败' : '成功');
            });
            // 这里需要给game发踢掉线指令
        }

        // 合区后多号
        if( req.method == 'get_uid' ) {

            var resp = {code:0, desc:'', data:{}, args:req};
            var uids = gMutiUsers[req.openid];
            var uidArr = [];
            for (var sid in uids) {
                uidArr.push(uids[sid]);
            }
            resp.data = uidArr;

            res.writeHead(200, common.defaultHeaders);
            res.end(JSON.stringify(resp));
            return;
        }

        // 合区后换号
        if( req.method == 'change_uid' ) {
            var succ = true;
            do {
                var uids = gMutiUsers[openid];
                if (!uids) {
                    console.log('shit');
                    succ = false;
                    break;
                }

                if (isNaN(req.new_uid)) {
                    console.log('shit new_uid');
                    succ = false;
                    break;
                }
              
                var newUid = +req.new_uid;
                var exist = false;
                for (var sid in uids) {
                    if (newUid == uids[sid]) {
                        exist = true;
                        break;
                    }
                }

                if (!exist) {
                    console.log('shit uid');
                    succ = false;
                    break;
                    
                }

                gUsers[openid] = newUid;
                gDBPlat.update({_id:openid},{$set:{'uid': newUid}}, function(err, result){});
            }while(false);

            var resp = {code:0, desc:"成功"};
            if (!succ) {
                resp.code = 1;
                resp.desc = "失败";
            }
            res.writeHead(200, common.defaultHeaders);
            res.end(JSON.stringify(resp));

            return;
        }

        // 禁止GM通过status接口编辑玩家元宝
        if ( req.method == 'status' && req.type == 'cash') {
            res.writeHead(200, common.defaultHeaders);
            res.end('该操作被禁止！');
            return;
        }

        // 操作玩家数据的GM指令
        var act = 'gm';
        if( req.method == 'pay' ) {
            act = 'pay';
        }

        if( req.method == 'diamond' ) {
            act = 'market_diamond';
        }

        var url = util.format('%s?mod=gm&act=%s&openid=%s&uid=%d&args=%j', getGameServer(existUid), act, openid, existUid, req);
        common.httpGet(url, function(data){
            try{
                data = JSON.parse(data);
            }catch(error){
            }

            if( data && data.data && data.data.info ) {
                data.data.info.openid = openid;
                data.data.info.uid = existUid;
            }

            res.writeHead(200, common.defaultHeaders);
            res.end(JSON.stringify(data));
        });

        ///////////////////////////////////////////////////////////////////
    });

    /**************************************************************************/
};

// 获取QQ电脑管家登录状态
logic.get_qqpcmgr_status = function(req, res, resp){
    do {
        if (!(req.hasOwnProperty('openid')
            && req.hasOwnProperty('key'))) {
            resp.code = 1; resp.desc = 'invalid arguments'; break;
        }

        var openid = req.openid;
        var key = req.key;

        if (key != config.GMAuth) {
            resp.code = 1; resp.desc = 'invalid key'; break;
        }

        var url = util.format('http://%s/qqpcmgr_get_login_status.php?openid=%s&openkey=%s', gConfGlobalServer.GamexURL, openid, gOpenkeys[openid]);

        if (!config.IsDebug) {
            common.httpGet(url, function(data) {
                if (data && data.code == 0 && data.status == 2) {
                    resp.code = 0;
                } else {
                    resp.code = 1; resp.desc = 'can not get status or offline';
                }
                res.writeHead(200, common.defaultHeaders);
                res.end(JSON.stringify(resp));
            }, true);
        } else {
            resp.code = 0; break;
        }

        return;
    } while (false);

    res.writeHead(200, common.defaultHeaders);
    res.end(JSON.stringify(resp));
};

/**
 * 批量发道具
 * @param req
 * @param res
 * @param resp
 * @private
 */
function _batSend(gmUser, req, res, resp) {

    function _endError(errMsg) {
        resp.code = 1;
        resp.desc = errMsg;
        res.writeHead(200, common.defaultHeaders);
        res.end(JSON.stringify(resp));
    }

    LOG(req['itemsToSend']);
    LOG(req['playerToReceve']);

    var itemsToSend = req['itemsToSend'];
    var playerToReceve = req['playerToReceve'];
    try {
        itemsToSend = JSON.parse(itemsToSend);
        playerToReceve = JSON.parse(playerToReceve);
    } catch (e) {
        _endError("格式错误");
        return;
    }

    var resultArr = [];

    async.eachSeries(playerToReceve, function(openid, callback){
        _sendItemToPlayer(openid, callback);
    }, function(err){
        res.writeHead(200, common.defaultHeaders);
        res.end(JSON.stringify(resultArr));
    });

    /**
     * 给单个玩家发道具
     * @param openid
     * @param callback
     * @private
     */
    function _sendItemToPlayer(openid, callback) {
        var uid = _getUID(openid);
        var url = util.format('%s?mod=gm&act=bat_send&uid=%d&items=%j&args={}',
            getGameServer(uid), uid, itemsToSend
        );

        var logObj = {
            gmUser: gmUser,
            ip: req["clientIP"],
            method: req.method,
            req: itemsToSend,
            openid: openid,
            uid: uid
        };

        common.httpGet(url, function(data){
            try{
                data = JSON.parse(data);
            }catch(error){
            }

            if( data && data.data && data.data.info ) {
                data.data.info.openid = openid;
                data.data.info.uid = uid;
            }

            resultArr.push(data);

            // 记录GM动作
            logObj['response'] = data;
            logGMActive(logObj);

            callback(null);
        });
    }

    /**
     * 获取玩家UID
     * @param openid
     * @returns {*}
     * @private
     */
    function _getUID(openid) {
        return gUsers[openid];
    }
}

/**
 * GM 用户登录
 * @param req
 * @param res
 * @param resp
 */
logic.gm_login = function(req, res, resp){
    var account = req["account"];
    var pass = req["pass"];

    if (account && pass) {
        var md5 = crypto.createHash("md5");
        pass = md5.update(pass).digest('hex');

        var gmUser = null;
        var authTime = null;
        var authKey = null;
        var modPrivilege = null;
        async.series([
            function(cb){ // 查询用户
                queryGMUser(account, function(err, res){
                    if (err || res == null) {
                        cb(null);
                    } else {
                        if (res["password"] == pass) {
                            gmUser = res;
                        }
                        cb(null);
                    }
                });
            },
            function(cb) { // 计算登录验证key
                if (gmUser == null) {
                    resp.code = 1;
                    resp.desc = "USERNAME OR PASSWORD ERROR!";
                    cb(util.format("[GM - %s] login failed!", account));
                    return;
                }

                authTime = common.getTime();
                authKey = calcAuthKey(authTime, pass);

                gmUser["ltime"] = authTime;
                cb(null);
            },
            function(cb){ // 查询模块权限
                loadAllModelPrivilege(function(err, res){
                    if (err) cb(err);
                    else {
                        modPrivilege = res;
                        cb(null);
                    }
                });
            },
            function(cb) { // 更新登录时间和IP
                var dbConn = mysql.createConnection(gmConfig.GMMysql);
                dbConn.connect(function(err){
                    if (err) {
                        cb(err);
                    } else {
                        dbConn.query(
                            mysql.format(
                                "UPDATE user SET ltime=?,ip=? WHERE uname=?",
                                [authTime, req.clientIP, account]
                            ),
                            function(err, res){
                                if (err) {
                                    cb(err);
                                } else {
                                    cb(null);
                                }
                                dbConn.end();
                            }
                        );
                    }
                });
            }
        ], function(err){

            // 记录错误信息
            if (err) {
                LOG(err);
            }

            // 返回数据到前台
            if (resp.code == 0) {
                resp.data["authKey"] = authKey;
                resp.data["account"] = account;
                resp.data["privilege"] = gmUser["plevel"];
                resp.data["modPrivileges"] = modPrivilege;
            }
            res.writeHead(200, common.defaultHeaders);
            res.end(JSON.stringify(resp));
        });

    } else { // 没有给出用户名或者密码数据
        resp.code = 1;
        resp.desc = "NO USERNAME AND PASSWORD!";
        res.writeHead(200, common.defaultHeaders);
        res.end(JSON.stringify(resp));
    }
};

function getFriends(friends, callback) {
    if( !friends ) {
        callback([]);
        return;
    }

    friends = friends.split(",");

    var fuids = [];
    var friendOpenids = {};
    for( var i=0; i<friends.length; i++ ) {
        var fopenid = friends[i];
        var fuid = gUsers[fopenid];
        if( fuid ) {
            fuids.push(fuid);
            friendOpenids[fuid] = fopenid;
        }
    }
    var friendDatas = [];
    var cursor = gDBUser.find({_id:{"$in":fuids}}, {'status':1}).sort({'status.level':-1});
    cursor.each(function(err, item){
        if( cursor.isClosed() ) {
            callback(friendDatas);
        }else{
            if( !item ) return;
            
            var friend = {};
            friend.fight_force = item.status.fight_force;
            friend.level = item.status.level;
            friend.uid = item._id;
            friend.openid = friendOpenids[friend.uid];
            
            friendDatas.push(friend);
        }
    });
}

function onLogIn(req, res, resp, uid) {
    var auth = common.genAuth(uid);

    resp.data.auth_key = auth.key;
    resp.data.auth_time = auth.time;
    resp.data.uid = uid;
    resp.data.game_server = getGameServer(uid);
    resp.data.friends = [];
    resp.data.invited = gUsers[req.invited] || 0;

    if( res._https ) {
        resp.data.game_server = util.format('https://%s:%s/', config.ServerHost, config.GameListen+1000);
    }
    
    onReqHandled(res, resp);
}

global.getGameServer = function(uid) {
    return util.format('http://%s:%s/', config.ServerHost, config.GameListen);
}

///////////////////////////////////////////////////////////////////////////////

function apply(object, config) {
    if (object && config && typeof config === 'object') {
        for (key in config) {
            if (config.hasOwnProperty(key)) {
                object[key] = config[key];
            }
        }
    }
    return object;
}

/**
 * 计算AuthKey
 * @param time
 * @param pass
 * @returns {string}
 */
function calcAuthKey(time, pass) {
    var md5 = crypto.createHash('md5');
    var timeHashStr = md5.update(time + "").digest('hex') + "";
    return timeHashStr.substr(0, 8) + pass.substr(0, 8);
}

/**
 * 检查GM登录信息
 * @param gmUser
 * @param authKey
 * @param callback
 * @returns {boolean}
 */
function checkGMAuth(gmUser, authKey, callback) {
    var checkPassed = false;
    async.series([
        function(cb) {
            if (gmUser) {
                var lastLoginTime = +(gmUser["ltime"]);
                var timePassed = common.getTime() - lastLoginTime;
                LOG(util.format("lastLoginTime:%d; currentTime:%d; timePassed:%s",
                    lastLoginTime, common.getTime(), timePassed));
                if (timePassed >= 0 && timePassed < gmConfig.authExpireTime) { // 还在登录有效期
                    var checkKey = calcAuthKey(lastLoginTime, gmUser["password"]);
                    checkPassed = (checkKey == authKey);
                    LOG(util.format("checkKey:%s; authKey:%s", checkKey, authKey));
                }
            }
            cb(null);
        }
    ], function(err){
        if (err) {
            LOG(err);
        }
        callback(checkPassed);
    });
}

/**
 * 从数据库加载模块权限配置
 * @param callback
 */
function loadAllModelPrivilege(callback) {
    var dbConn = mysql.createConnection(gmConfig.GMMysql);
    dbConn.connect(function(err){
        if (err) {
            callback(err);
        } else {
            dbConn.query(
                "SELECT * FROM model",
                function(err, rows){
                    if (err) {
                        callback(err);
                    } else {
                        var modPrivilegeObj = {};
                        if (rows.length > 0) {
                            for (var key in rows) {
                                if (rows.hasOwnProperty(key)) {
                                    cacheModelPrivilege(modPrivilegeObj, rows[key]);
                                }
                            }
                        }
                        callback(null, modPrivilegeObj);
                    }
                    dbConn.end();
                }
            );
        }
    });
}

/**
 * 查询GM用户信息
 * @param account
 * @param callback
 */
function queryGMUser(account, callback) {
    // 从数据库查询用户
    var dbConn = mysql.createConnection(gmConfig.GMMysql);
    dbConn.connect(function(err){
        if (err) {
            callback(err, null);
        } else {
            dbConn.query(
                mysql.format("SELECT * FROM user WHERE uname=?",[account]),
                function(err, rows, fields){
                    if (err) {
                        callback(err, null);
                    } else {
                        if (rows.length > 0) {
                            callback(null, rows[0]);
                        } else {
                            callback(null, null);
                        }
                    }
                    dbConn.end();
                }
            );
        }
    });
}

/**
 * 检查GM用户时候有权限访问指定的资源
 * @param gmUser
 * @param mod
 * @param act
 * @param modPrivilegeObj
 * @param callback
 */
function checkGMPrivilege(gmUser, mod, act, modPrivilegeObj,callback) {
    var gmUserPLevel = +gmUser["plevel"];
    if (gmUserPLevel <=0) {
        callback(false);
        return;
    }

    var modAct = mod + ":" + act;
    var modActPConfig = modPrivilegeObj[modAct];
    if (modActPConfig) {
        callback(gmUserPLevel in modActPConfig["allowplevel"]);
    } else { // 这个资源没有配置访问权限
        callback(false);
    }
}

/**
 * 缓存模块权限
 * @param modPrivilegeObj
 * @param mp
 * @returns {*}
 */
function cacheModelPrivilege(modPrivilegeObj, mp) {
    var allowLevel = JSON.parse(mp["allowplevel"]);
    var allowLevels = {};
    for (var key in allowLevel) {
        if (allowLevel.hasOwnProperty(key)) {
            allowLevels[allowLevel[key]] = 1;
        }
    }
    mp["allowplevel"] = allowLevels;
    modPrivilegeObj[mp["modact"]] = mp;
    return mp;
}

/**
 * 记录GM操作日志
 * @param gmActive
 */
function logGMActive(gmActive) {

    switch (gmActive.method) {
        case "get":
        case "message":
        case "get_uid":
            return;
        default :
            break;
    }

    //var mongoServer = new mongodb.Server(
    //    gmConfig.GMMongo.host, gmConfig.GMMongo.port);
    //var db = new mongodb.Db(gmConfig.GMMongo.db, mongoServer,
    //    {'native_parser':true, 'w':1, 'wtimeout':10, 'fsync':true});
    //db.open(function(err, db) {
    //    if( err ) {
    //        ERROR(err);
    //    } else {
    //        var dbLog = db.collection('log');
    //        dbLog.insert({
    //                sid: config.ServerId,
    //                name: gmActive.gmUser["uname"],
    //                ip: gmActive.ip,
    //                method: gmActive.method,
    //                openid: gmActive.openid,
    //                uid:gmActive.uid,
    //                req: gmActive.req,
    //                time:common.getTime()
    //            },
    //            function(err, result){
    //                db.close();
    //            });
    //    }
    //});

    var dbConn = mysql.createConnection(gmConfig.GMMysql);
    dbConn.connect(function(err){
        if (!err) {
            var actResult = gmActive['response'];
            if (actResult) {
                if (typeof actResult == 'object') {
                    actResult = JSON.stringify(actResult);
                } else {
                    actResult = actResult + '';
                }
            }
            var SQL = mysql.format(
                "INSERT INTO log VALUES (0,?,?,?,?,?,?,?,?,?)",
                [config.ServerId + '', gmActive.gmUser["uname"], gmActive.ip, gmActive.method,
                    gmActive.openid + '', gmActive.uid + '', JSON.stringify(gmActive.req),
                    common.getTime(), actResult]
            );
            dbConn.query(
                SQL,
                function(err, res){
                    dbConn.end();
                }
            );
        }
    });
}
